/*
REWRITTEN BY XINEF
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "the_eye.h"
#include "WorldPacket.h"
#include "Opcodes.h"

enum Yells
{
    // Kael'thas Speech
    SAY_INTRO                           = 0,
    SAY_INTRO_CAPERNIAN                 = 1,
    SAY_INTRO_TELONICUS                 = 2,
    SAY_INTRO_THALADRED                 = 3,
    SAY_INTRO_SANGUINAR                 = 4,
    SAY_PHASE2_WEAPON                   = 5,
    SAY_PHASE3_ADVANCE                  = 6,
    SAY_PHASE4_INTRO2                   = 7,
    SAY_PHASE5_NUTS                     = 8,
    SAY_SLAY                            = 9,
    SAY_MINDCONTROL                     = 10,
    SAY_GRAVITYLAPSE                    = 11,
    SAY_SUMMON_PHOENIX                  = 12,
    SAY_DEATH                           = 13,
	SAY_PYROBLAST						= 14,

    // Advisors
    SAY_THALADRED_AGGRO                 = 0,
    SAY_SANGUINAR_AGGRO                 = 0,
    SAY_CAPERNIAN_AGGRO                 = 0,
    SAY_TELONICUS_AGGRO                 = 0
};

enum Spells
{
    // Phase 2 spells
    SPELL_SUMMON_WEAPONS                = 36976,
    SPELL_SUMMON_WEAPONA                = 36958,
    SPELL_SUMMON_WEAPONB                = 36959,
    SPELL_SUMMON_WEAPONC                = 36960,
    SPELL_SUMMON_WEAPOND                = 36961,
    SPELL_SUMMON_WEAPONE                = 36962,
    SPELL_SUMMON_WEAPONF                = 36963,
    SPELL_SUMMON_WEAPONG                = 36964,

	// Phase 3 spells
	SPELL_RESURRECTION					= 36450,

    // Phase 4 spells
    SPELL_FIREBALL                      = 36805,
    SPELL_ARCANE_DISRUPTION             = 36834,
    SPELL_PHOENIX						= 36723,
    SPELL_MIND_CONTROL                  = 36797,
    SPELL_SHOCK_BARRIER                 = 36815,
    SPELL_PYROBLAST                     = 36819,
    SPELL_FLAME_STRIKE                  = 36735,
    SPELL_FLAME_STRIKE_DAMAGE           = 36731,

	// Event
	SPELL_NETHERBEAM_AURA1				= 36364,
	SPELL_NETHERBEAM_AURA2				= 36370,
	SPELL_NETHERBEAM_AURA3				= 36371,
	SPELL_NETHERBEAM1					= 36089,
	SPELL_NETHERBEAM2					= 36090,
	SPELL_KAEL_GAINING_POWER			= 36091,
	SPELL_KAEL_EXPLODES1				= 36376,
	SPELL_KAEL_EXPLODES2				= 36375,
	SPELL_KAEL_EXPLODES3				= 36373,
	SPELL_KAEL_EXPLODES4				= 36354,
	SPELL_KAEL_EXPLODES5				= 36092,
	SPELL_GROW							= 36184,
	SPELL_KEAL_STUNNED					= 36185,
	SPELL_KAEL_FULL_POWER				= 36187,
	SPELL_FLOATING_DROWNED				= 36550,

	SPELL_PURE_NETHER_BEAM1				= 36196,
	SPELL_PURE_NETHER_BEAM2				= 36197,
	SPELL_PURE_NETHER_BEAM3				= 36198,

    // Phase 5 spells
	SPELL_GRAVITY_LAPSE					= 35941,
	SPELL_GRAVITY_LAPSE_TELEPORT1		= 35966,
	SPELL_GRAVITY_LAPSE_KNOCKBACK		= 34480,
	SPELL_GRAVITY_LAPSE_AURA			= 39432,
	SPELL_SUMMON_NETHER_VAPOR			= 35865,
	SPELL_NETHER_BEAM					= 35869,
	SPELL_NETHER_BEAM_DAMAGE			= 35873,


	SPELL_REMOTE_TOY_STUN				= 37029
};

enum Misc
{
	POINT_MIDDLE						= 1,
	POINT_AIR							= 2,
	POINT_START_LAST_PHASE				= 3,
	DATA_RESURRECT_CAST					= 1,
	NPC_WORLD_TRIGGER					= 19871,
	NPC_NETHER_VAPOR					= 21002,

	PHASE_NONE							= 0,
	PHASE_SINGLE_ADVISOR				= 1,
	PHASE_WEAPONS						= 2,
	PHASE_ALL_ADVISORS					= 3,
	PHASE_FINAL							= 4,

	EVENT_PREFIGHT_PHASE11				= 1,
	EVENT_PREFIGHT_PHASE12				= 2,
	EVENT_PREFIGHT_PHASE21				= 3,
	EVENT_PREFIGHT_PHASE22				= 4,
	EVENT_PREFIGHT_PHASE31				= 5,
	EVENT_PREFIGHT_PHASE32				= 6,
	EVENT_PREFIGHT_PHASE41				= 7,
	EVENT_PREFIGHT_PHASE42				= 8,
	EVENT_PREFIGHT_PHASE51				= 9,
	EVENT_PREFIGHT_PHASE52				= 10,
	EVENT_PREFIGHT_PHASE61				= 11,
	EVENT_PREFIGHT_PHASE62				= 12,
	EVENT_PREFIGHT_PHASE63				= 13,
	EVENT_PREFIGHT_PHASE71				= 14,
	EVENT_GATHER_ADVISORS				= 15,

	EVENT_SPELL_SEQ_1					= 30,
	EVENT_SPELL_SEQ_2					= 31,
	EVENT_SPELL_SEQ_3					= 32,
	EVENT_SPELL_FIREBALL				= 33,
	EVENT_SPELL_PYROBLAST				= 34,
	EVENT_SPELL_FLAMESTRIKE				= 35,
	EVENT_SPELL_ARCANE_DISRUPTION		= 36,
	EVENT_SPELL_MIND_CONTROL			= 37,
	EVENT_SPELL_SUMMON_PHOENIX			= 38,
	EVENT_CHECK_HEALTH					= 39,
	EVENT_SPELL_GRAVITY_LAPSE			= 40,
	EVENT_GRAVITY_LAPSE_END				= 41,
	EVENT_SPELL_SHOCK_BARRIER			= 42,
	EVENT_SPELL_NETHER_BEAM				= 43,
	EVENT_SPELL_NETHER_VAPOR			= 44,

	EVENT_SCENE_1						= 50,
	EVENT_SCENE_2						= 51,
	EVENT_SCENE_3						= 52,
	EVENT_SCENE_4						= 53,
	EVENT_SCENE_5						= 54,
	EVENT_SCENE_6						= 55,
	EVENT_SCENE_7						= 56,
	EVENT_SCENE_8						= 57,
	EVENT_SCENE_9						= 58,
	EVENT_SCENE_10						= 59,
	EVENT_SCENE_11						= 60,
	EVENT_SCENE_12						= 61,
	EVENT_SCENE_13						= 62,
	EVENT_SCENE_14						= 63,
	EVENT_SCENE_15						= 64,
	EVENT_SCENE_16						= 65
};

const Position triggersPos[6] =
{
	{799.11f, -38.95f, 85.0f, 0.0f},
	{800.16f, 37.65f, 85.0f, 0.0f},
	{847.64f, -16.19f, 64.05f, 0.0f},
	{847.53f, 15.01f, 63.69f, 0.0f},
	{843.44f, -7.87f, 67.14f, 0.0f},
	{843.35f, 6.35f, 67.14f, 0.0f}
};

class boss_kaelthas : public CreatureScript
{
    public:
		boss_kaelthas() : CreatureScript("boss_kaelthas") { }

        struct boss_kaelthasAI : public BossAI
        {
            boss_kaelthasAI(Creature* creature) : BossAI(creature, DATA_KAELTHAS) { }

			uint8 phase;
			EventMap events2;
            
			void PrepareAdvisors()
			{
				for (uint8 i = DATA_KAEL_ADVISOR1; i <= DATA_KAEL_ADVISOR4; ++i)
					if (Creature* advisor = ObjectAccessor::GetCreature(*me, instance->GetData64(i)))
					{
						advisor->Respawn(true);
						advisor->StopMovingOnCurrentPos();
						advisor->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
						advisor->SetReactState(REACT_PASSIVE);
						summons.Summon(advisor);
					}
			}

			void SetData(uint32 type, uint32 data)
			{
				if (type == DATA_RESURRECT_CAST && data == DATA_RESURRECT_CAST)
				{
					for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i)
						if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
							if (summon->GetDBTableGUIDLow())
							{
								summon->SetReactState(REACT_PASSIVE);
								summon->setDeathState(JUST_RESPAWNED);
								summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
							}
				}
			}

			void SetRoomState(GOState state)
			{
				if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_BRIDGE_WINDOW)))
					window->SetGoState(state);
				if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_KAEL_STATUE_RIGHT)))
					window->SetGoState(state);
				if (GameObject* window = ObjectAccessor::GetGameObject(*me, instance->GetData64(GO_KAEL_STATUE_LEFT)))
					window->SetGoState(state);
			}

            void Reset()
            {
				BossAI::Reset();
				events2.Reset();
				events2.ScheduleEvent(EVENT_GATHER_ADVISORS, 1000);
				phase = PHASE_NONE;

				me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_HOVER, true);
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_DISABLE_MOVE);
				SetRoomState(GO_STATE_READY);
				me->SetDisableGravity(false);
				me->SetWalk(false);
            }

			void AttackStart(Unit* who)
			{
				if (phase == PHASE_FINAL && events.GetNextEventTime(EVENT_GRAVITY_LAPSE_END) == 0)
					BossAI::AttackStart(who);
			}

			void MoveInLineOfSight(Unit* who)
            {
				if (phase == PHASE_NONE && who->GetTypeId() == TYPEID_PLAYER && me->IsValidAttackTarget(who))
				{
					phase = PHASE_SINGLE_ADVISOR;
					me->SetInCombatWithZone();
                    Talk(SAY_INTRO);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE11, 23000);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE12, 30000);
				}
            }

            void EnterCombat(Unit* who)
            {
				BossAI::EnterCombat(who);
            }

            void KilledUnit(Unit* victim)
            {
				if (victim->GetTypeId() == TYPEID_PLAYER)
					Talk(SAY_SLAY);
            }

            void JustSummoned(Creature* summon)
            {
				summons.Summon(summon);
				if (summon->GetEntry() == NPC_NETHER_VAPOR)
					summon->GetMotionMaster()->MoveRandom(20.0f);
            }

            void SummonedCreatureDies(Creature* summon, Unit*)
            {
				if (phase == PHASE_FINAL)
					return;

				if (summon->GetDBTableGUIDLow() && phase == PHASE_ALL_ADVISORS)
				{
					for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i)
						if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
							if (summon->GetDBTableGUIDLow() && summon->IsAlive())
								return;

					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 2000);
					return;
				}

				if (summon->GetEntry() == NPC_THALADRED)
				{
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE21, 2000);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE22, 14500);
				}
				else if (summon->GetEntry() == NPC_LORD_SANGUINAR)
				{
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE31, 2000);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE32, 9000);
				}
				else if (summon->GetEntry() == NPC_CAPERNIAN)
				{
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE41, 2000);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE42, 10400);
				}
				else if (summon->GetEntry() == NPC_TELONICUS)
				{
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE51, 3000);
					events2.ScheduleEvent(EVENT_PREFIGHT_PHASE52, 9000);
				}
            }

            void JustDied(Unit* killer)
            {
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE);

                Talk(SAY_DEATH);
				BossAI::JustDied(killer);
            }

			void MovementInform(uint32 type, uint32 point)
			{
				if (type != POINT_MOTION_TYPE)
					return;

				if (point == POINT_MIDDLE)
				{
					events2.ScheduleEvent(EVENT_SCENE_1, 0);
					events2.ScheduleEvent(EVENT_SCENE_2, 2500);
					events2.ScheduleEvent(EVENT_SCENE_3, 4000);
					events2.ScheduleEvent(EVENT_SCENE_4, 7000);
					events2.ScheduleEvent(EVENT_SCENE_5, 10000);
					events2.ScheduleEvent(EVENT_SCENE_6, 14000);
					events2.ScheduleEvent(EVENT_SCENE_7, 17500);
					events2.ScheduleEvent(EVENT_SCENE_8, 19000);
					events2.ScheduleEvent(EVENT_SCENE_9, 22000); // two first lightnings + aura
					events2.ScheduleEvent(EVENT_SCENE_10, 22800); // two
					events2.ScheduleEvent(EVENT_SCENE_11, 23600); // two
					events2.ScheduleEvent(EVENT_SCENE_12, 24500); // two
					events2.ScheduleEvent(EVENT_SCENE_13, 24800); // two
					events2.ScheduleEvent(EVENT_SCENE_14, 25300); // two
					events2.ScheduleEvent(EVENT_SCENE_15, 32000); // full power
					events2.ScheduleEvent(EVENT_SCENE_16, 36000); // remove lightnings + aura, move down
				}
				else if (point == POINT_START_LAST_PHASE)
				{
					me->SetDisableGravity(false);
					me->SetWalk(false);
					me->RemoveAurasDueToSpell(SPELL_KAEL_FULL_POWER);
					me->SetReactState(REACT_AGGRESSIVE);
					me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
					events.SetTimer(60000);
					events.ScheduleEvent(EVENT_SPELL_FIREBALL, 0);
					events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 10000);
					events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 20000);
					events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 5000);
					if (me->GetVictim())
					{
						me->SetTarget(me->GetVictim()->GetGUID());
						AttackStart(me->GetVictim());
					}
				}
			}

            void UpdateAI(uint32 diff)
            {
				if (EnterEvadeIfOutOfCombatArea())
					return;

				events2.Update(diff);
				switch (events2.ExecuteEvent())
				{
					case EVENT_GATHER_ADVISORS:
						PrepareAdvisors();
						break;
					case EVENT_PREFIGHT_PHASE11:
						Talk(SAY_INTRO_THALADRED);
						break;
					case EVENT_PREFIGHT_PHASE12:
						if (Creature* advisor = summons.GetCreatureWithEntry(NPC_THALADRED))
						{
							advisor->SetReactState(REACT_AGGRESSIVE);
							advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
							if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
								advisor->AI()->AttackStart(target);
							advisor->SetInCombatWithZone();
							advisor->AI()->Talk(SAY_THALADRED_AGGRO);
						}
						break;
					case EVENT_PREFIGHT_PHASE21:
						Talk(SAY_INTRO_SANGUINAR);
						break;
					case EVENT_PREFIGHT_PHASE22:
						if (Creature* advisor = summons.GetCreatureWithEntry(NPC_LORD_SANGUINAR))
						{
							advisor->SetReactState(REACT_AGGRESSIVE);
							advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
							if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
								advisor->AI()->AttackStart(target);
							advisor->SetInCombatWithZone();
							advisor->AI()->Talk(SAY_SANGUINAR_AGGRO);
						}
						break;
					case EVENT_PREFIGHT_PHASE31:
						Talk(SAY_INTRO_CAPERNIAN);
						break;
					case EVENT_PREFIGHT_PHASE32:
						if (Creature* advisor = summons.GetCreatureWithEntry(NPC_CAPERNIAN))
						{
							advisor->SetReactState(REACT_AGGRESSIVE);
							advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
							if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
								advisor->AI()->AttackStart(target);
							advisor->SetInCombatWithZone();
							advisor->AI()->Talk(SAY_CAPERNIAN_AGGRO);
						}
						break;
					case EVENT_PREFIGHT_PHASE41:
						Talk(SAY_INTRO_TELONICUS);
						break;
					case EVENT_PREFIGHT_PHASE42:
						if (Creature* advisor = summons.GetCreatureWithEntry(NPC_TELONICUS))
						{
							advisor->SetReactState(REACT_AGGRESSIVE);
							advisor->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
							if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
								advisor->AI()->AttackStart(target);
							advisor->SetInCombatWithZone();
							advisor->AI()->Talk(SAY_TELONICUS_AGGRO);
						}
						break;
					case EVENT_PREFIGHT_PHASE51:
						Talk(SAY_PHASE2_WEAPON);
						me->CastSpell(me, SPELL_SUMMON_WEAPONS, false);
						phase = PHASE_WEAPONS;
						break;
					case EVENT_PREFIGHT_PHASE52:
						for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i)
						{
							if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
								if (!summon->GetDBTableGUIDLow())
								{
									summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_NON_ATTACKABLE);
									summon->SetInCombatWithZone();
									if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
										summon->AI()->AttackStart(target);
								}
						}
						events2.ScheduleEvent(EVENT_PREFIGHT_PHASE61, 2*MINUTE*IN_MILLISECONDS);
						events2.ScheduleEvent(EVENT_PREFIGHT_PHASE62, 2*MINUTE*IN_MILLISECONDS+6000);
						events2.ScheduleEvent(EVENT_PREFIGHT_PHASE63, 2*MINUTE*IN_MILLISECONDS+12000);
						break;
					case EVENT_PREFIGHT_PHASE61:
						phase = PHASE_ALL_ADVISORS;
						Talk(SAY_PHASE3_ADVANCE);
						break;
					case EVENT_PREFIGHT_PHASE62:
						me->CastSpell(me, SPELL_RESURRECTION, false);
						break;
					case EVENT_PREFIGHT_PHASE63:
						for (SummonList::const_iterator i = summons.begin(); i != summons.end(); ++i)
							if (Creature* summon = ObjectAccessor::GetCreature(*me, *i))
								if (summon->GetDBTableGUIDLow())
								{
									summon->SetReactState(REACT_AGGRESSIVE);
									summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
									summon->SetInCombatWithZone();
									if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
										summon->AI()->AttackStart(target);
								}
						events2.ScheduleEvent(EVENT_PREFIGHT_PHASE71, 3*MINUTE*IN_MILLISECONDS);
						break;
					case EVENT_PREFIGHT_PHASE71:
						events2.CancelEvent(EVENT_PREFIGHT_PHASE71);
						Talk(SAY_PHASE4_INTRO2);
						phase = PHASE_FINAL;
						DoResetThreat();
						me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_DISABLE_MOVE);
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
                            AttackStart(target);

						events2.Reset();
						events.Reset();
						events.ScheduleEvent(EVENT_SPELL_FIREBALL, 1000);
						events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 15000);
						events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 30000);
						events.ScheduleEvent(EVENT_SPELL_SEQ_1, 20000);
						events.ScheduleEvent(EVENT_SPELL_SEQ_2, 40000);
						events.ScheduleEvent(EVENT_SPELL_SEQ_3, 60000);
						events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
						break;
					case EVENT_SCENE_1:
						me->SetTarget(0);
						me->SetFacingTo(M_PI);
						me->SetWalk(true);
						Talk(SAY_PHASE5_NUTS);
						break;
					case EVENT_SCENE_2:
						me->SetTarget(0);
						me->CastSpell(me, SPELL_KAEL_EXPLODES1, true);
						me->CastSpell(me, SPELL_KAEL_GAINING_POWER, false);
						me->SetDisableGravity(true);
						break;
					case EVENT_SCENE_3:
						me->SetTarget(0);
						for (uint8 i = 0; i < 2; ++i)
							if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i], TEMPSUMMON_TIMED_DESPAWN, 60000))
								trigger->CastSpell(me, SPELL_NETHERBEAM1+i, false);
						me->GetMotionMaster()->MovePoint(POINT_AIR, me->GetPositionX(), me->GetPositionY(), 76.0f, false, true);
						me->CastSpell(me, SPELL_GROW, true);
						break;
					case EVENT_SCENE_4:
						me->SetTarget(0);
						me->CastSpell(me, SPELL_GROW, true);
						me->CastSpell(me, SPELL_KAEL_EXPLODES2, true);
						me->CastSpell(me, SPELL_NETHERBEAM_AURA1, true);
						for (uint8 i = 0; i < 2; ++i)
							if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i+2], TEMPSUMMON_TIMED_DESPAWN, 60000))
								trigger->CastSpell(me, SPELL_NETHERBEAM1+i, false);
						break;
					case EVENT_SCENE_5:
						me->SetTarget(0);
						me->CastSpell(me, SPELL_GROW, true);
						me->CastSpell(me, SPELL_KAEL_EXPLODES3, true);
						me->CastSpell(me, SPELL_NETHERBEAM_AURA2, true);
						for (uint8 i = 0; i < 2; ++i)
							if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, triggersPos[i+4], TEMPSUMMON_TIMED_DESPAWN, 60000))
								trigger->CastSpell(me, SPELL_NETHERBEAM1+i, false);
						break;
					case EVENT_SCENE_6:
						me->CastSpell(me, SPELL_GROW, true);
						me->CastSpell(me, SPELL_KAEL_EXPLODES4, true);
						me->CastSpell(me, SPELL_NETHERBEAM_AURA3, true);
						break;
					case EVENT_SCENE_7:
						SetRoomState(GO_STATE_ACTIVE);
						me->SetUnitMovementFlags(MOVEMENTFLAG_HOVER|MOVEMENTFLAG_WALKING|MOVEMENTFLAG_DISABLE_GRAVITY);
						me->SendMovementFlagUpdate();
						break;
					case EVENT_SCENE_8:
						summons.DespawnEntry(WORLD_TRIGGER);
						me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA1);
						me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA2);
						me->RemoveAurasDueToSpell(SPELL_NETHERBEAM_AURA3);
						me->CastSpell(me, SPELL_KAEL_EXPLODES5, true);
						me->CastSpell(me, SPELL_FLOATING_DROWNED, false);
						//me->CastSpell(me, SPELL_KEAL_STUNNED, true);
						break;
					case EVENT_SCENE_9:
						me->CastSpell(me, 52241, true); // WRONG VISUAL, ZOMG!
						me->CastSpell(me, 34807, true);
						me->SummonCreature(NPC_WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000);
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()+5, me->GetPositionY(), me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true);
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY(), me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true);
						break;
					case EVENT_SCENE_10:
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()-5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true);
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()+5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true);
						break;
					case EVENT_SCENE_11:
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true);
						break;
					case EVENT_SCENE_12:
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX(), me->GetPositionY()-5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true);
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()+5, me->GetPositionY()-5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM1, true);
						break;
					case EVENT_SCENE_13:
						if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
							trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM2, true);
						break;
					case EVENT_SCENE_14:
						//if (Creature* trigger = me->SummonCreature(WORLD_TRIGGER, me->GetPositionX()-5, me->GetPositionY()+5, me->GetPositionZ()+15.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000))
						//	trigger->CastSpell(me, SPELL_PURE_NETHER_BEAM3, true);
						break;
					case EVENT_SCENE_15:
						me->RemoveAurasDueToSpell(SPELL_FLOATING_DROWNED);
						me->RemoveAurasDueToSpell(SPELL_KEAL_STUNNED);
						me->CastSpell(me, SPELL_KAEL_FULL_POWER, false);
						me->CastSpell(me, 36709, true);
						me->CastSpell(me, 36201, true);
						me->CastSpell(me, 36290, true);
						me->CastSpell(me, 36291, true);
						me->SetUnitMovementFlags(MOVEMENTFLAG_DISABLE_GRAVITY|MOVEMENTFLAG_WALKING);
						me->SendMovementFlagUpdate();
						break;
					case EVENT_SCENE_16:
						summons.DespawnEntry(WORLD_TRIGGER);
						me->RemoveAurasDueToSpell(52241); // WRONG VISUAL, ZOMG!
						me->GetMotionMaster()->MovePoint(POINT_START_LAST_PHASE, me->GetHomePosition(), false, true);
						break;
				}

				if (!events2.Empty())
					return;

				if (!UpdateVictim())
					return;

				events.Update(diff);
				if (me->HasUnitState(UNIT_STATE_CASTING))
					return;

				switch (events.ExecuteEvent())
				{
					case EVENT_SPELL_SEQ_1:
						events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 0);
						events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 3000);
						events.ScheduleEvent(EVENT_SPELL_SEQ_1, 50000);
						break;
					case EVENT_SPELL_SEQ_2:
						events.ScheduleEvent(EVENT_SPELL_MIND_CONTROL, 3000);
						events.ScheduleEvent(EVENT_SPELL_ARCANE_DISRUPTION, 6000);
						events.ScheduleEvent(EVENT_SPELL_SEQ_2, 50000);
						break;
					case EVENT_SPELL_SEQ_3:
						Talk(SAY_PYROBLAST);
						me->CastSpell(me, SPELL_SHOCK_BARRIER, false);
						events.ScheduleEvent(EVENT_SPELL_SEQ_3, 50000);
						events.DelayEvents(10000);
						events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 0);
						events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 4000);
						events.ScheduleEvent(EVENT_SPELL_PYROBLAST, 8000);
						break;
					case EVENT_SPELL_SHOCK_BARRIER:
						me->CastSpell(me, SPELL_SHOCK_BARRIER, false);
						break;
					case EVENT_SPELL_FIREBALL:
						me->CastSpell(me->GetVictim(), SPELL_FIREBALL, false);
						events.ScheduleEvent(EVENT_SPELL_FIREBALL, roll_chance_i(70) ? 2000: 4000);
						break;
					case EVENT_SPELL_PYROBLAST:
						me->CastSpell(me->GetVictim(), SPELL_PYROBLAST, false);
						break;
					case EVENT_SPELL_FLAMESTRIKE:
						if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true))
							me->CastSpell(target, SPELL_FLAME_STRIKE, false);
						events.ScheduleEvent(EVENT_SPELL_FLAMESTRIKE, 20000);
						break;
					case EVENT_SPELL_ARCANE_DISRUPTION:
						me->CastSpell(me, SPELL_ARCANE_DISRUPTION, false);
						break;
					case EVENT_SPELL_MIND_CONTROL:
						if (roll_chance_i(50))
							Talk(SAY_MINDCONTROL);
						me->CastCustomSpell(SPELL_MIND_CONTROL, SPELLVALUE_MAX_TARGETS, 3, me, false);
						break;
					case EVENT_SPELL_SUMMON_PHOENIX:
						Talk(SAY_SUMMON_PHOENIX);
						me->CastSpell(me, SPELL_PHOENIX, false);
						events.ScheduleEvent(EVENT_SPELL_SUMMON_PHOENIX, 40000);
						break;
					case EVENT_CHECK_HEALTH:
						if (me->HealthBelowPct(51))
						{
							events.Reset();
							me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
							me->SetReactState(REACT_PASSIVE);
							me->GetMotionMaster()->MovePoint(POINT_MIDDLE, me->GetHomePosition(), true, true); 
							me->ClearUnitState(UNIT_STATE_MELEE_ATTACKING);
							me->SendMeleeAttackStop();
							break;
						}
						events.ScheduleEvent(EVENT_CHECK_HEALTH, 1000);
						break;
					case EVENT_SPELL_GRAVITY_LAPSE:
						events.DelayEvents(30000);
						me->setAttackTimer(BASE_ATTACK, 30000);
						events.ScheduleEvent(EVENT_SPELL_GRAVITY_LAPSE, 90000);
						events.ScheduleEvent(EVENT_GRAVITY_LAPSE_END, 32000);
						events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 20000);
						events.ScheduleEvent(EVENT_SPELL_SHOCK_BARRIER, 10000);
						events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000);
						events.ScheduleEvent(EVENT_SPELL_NETHER_VAPOR, 0);
						me->CastSpell(me, SPELL_SHOCK_BARRIER, false);
						me->CastSpell(me, SPELL_GRAVITY_LAPSE, false);
						me->SetTarget(0);
						me->GetMotionMaster()->Clear();
						me->StopMoving();
						Talk(SAY_GRAVITYLAPSE);
						break;
					case EVENT_SPELL_NETHER_VAPOR:
						me->CastSpell(me, SPELL_SUMMON_NETHER_VAPOR, false);
						break;
					case EVENT_SPELL_NETHER_BEAM:
						me->CastSpell(me, SPELL_NETHER_BEAM, false);
						events.ScheduleEvent(EVENT_SPELL_NETHER_BEAM, 4000);
						break;
					case EVENT_GRAVITY_LAPSE_END:
						summons.DespawnEntry(NPC_NETHER_VAPOR);
						events.CancelEvent(EVENT_SPELL_NETHER_BEAM);
						me->SetTarget(me->GetVictim()->GetGUID());
						me->GetMotionMaster()->MoveChase(me->GetVictim());
						break;
				}

				DoMeleeAttackIfReady();
            }

			bool CheckEvadeIfOutOfCombatArea() const
			{
				return me->GetHomePosition().GetExactDist2d(me) > 165.0f || !SelectTargetFromPlayerList(165.0f);
			}

        };
        CreatureAI* GetAI(Creature* creature) const
        {
            return GetInstanceAI<boss_kaelthasAI>(creature);
        }
};

class spell_kaelthas_kael_phase_two : public SpellScriptLoader
{
    public:
        spell_kaelthas_kael_phase_two() : SpellScriptLoader("spell_kaelthas_kael_phase_two") { }

        class spell_kaelthas_kael_phase_two_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_kael_phase_two_SpellScript);

            bool Load()
            {
				if (GetCaster()->GetTypeId() == TYPEID_UNIT)
					if (InstanceScript* instance = GetCaster()->GetInstanceScript())
						if (Creature* kael = ObjectAccessor::GetCreature(*GetCaster(), instance->GetData64(NPC_KAELTHAS)))
							kael->AI()->SummonedCreatureDies(GetCaster()->ToCreature(), NULL);
				return true;
            }

            void Register()
            {
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_kael_phase_two_SpellScript();
        }
};

class spell_kaelthas_remote_toy : public SpellScriptLoader
{
    public:
        spell_kaelthas_remote_toy() : SpellScriptLoader("spell_kaelthas_remote_toy") { }

        class spell_kaelthas_remote_toy_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_kaelthas_remote_toy_AuraScript);

            void HandlePeriodic(AuraEffect const* aurEff)
            {
                PreventDefaultAction();
				if (roll_chance_i(66))
					GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_REMOTE_TOY_STUN, true);
            }

            void Register()
            {
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_kaelthas_remote_toy_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_kaelthas_remote_toy_AuraScript();
        }
};

class spell_kaelthas_summon_weapons : public SpellScriptLoader
{
    public:
        spell_kaelthas_summon_weapons() : SpellScriptLoader("spell_kaelthas_summon_weapons") { }

        class spell_kaelthas_summon_weapons_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_summon_weapons_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
				PreventHitEffect(effIndex);
				for (uint32 i = SPELL_SUMMON_WEAPONA; i <= SPELL_SUMMON_WEAPONG; ++i)
					GetCaster()->CastSpell(GetCaster(), i, true);				
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_kaelthas_summon_weapons_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_summon_weapons_SpellScript();
        }
};

class spell_kaelthas_resurrection : public SpellScriptLoader
{
	public:
		spell_kaelthas_resurrection() : SpellScriptLoader("spell_kaelthas_resurrection") { }

		class spell_kaelthas_resurrection_SpellScript : public SpellScript
		{
			PrepareSpellScript(spell_kaelthas_resurrection_SpellScript);

			void HandleBeforeCast()
			{
				GetCaster()->GetAI()->SetData(DATA_RESURRECT_CAST, DATA_RESURRECT_CAST);
			}

			void Register()
			{
				BeforeCast += SpellCastFn(spell_kaelthas_resurrection_SpellScript::HandleBeforeCast);
			}
		};

		SpellScript* GetSpellScript() const
		{
			return new spell_kaelthas_resurrection_SpellScript();
		}
};

class spell_kaelthas_mind_control : public SpellScriptLoader
{
    public:
        spell_kaelthas_mind_control() : SpellScriptLoader("spell_kaelthas_mind_control") { }

		class spell_kaelthas_mind_control_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_mind_control_SpellScript);

            void SelectTarget(std::list<WorldObject*>& targets)
            {
				if (Unit* victim = GetCaster()->GetVictim())
					targets.remove_if(Trinity::ObjectGUIDCheck(victim->GetGUID(), true));
            }

            void Register()
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_kaelthas_mind_control_SpellScript::SelectTarget, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_mind_control_SpellScript();
        }
};

class spell_kaelthas_burn : public SpellScriptLoader
{
    public:
        spell_kaelthas_burn() : SpellScriptLoader("spell_kaelthas_burn") { }

        class spell_kaelthas_burn_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_kaelthas_burn_AuraScript);

            void HandlePeriodic(AuraEffect const* aurEff)
            {
				Unit::DealDamage(GetUnitOwner(), GetUnitOwner(), GetUnitOwner()->CountPctFromMaxHealth(5)+1);
            }

            void Register()
            {
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_kaelthas_burn_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
            }
        };

        AuraScript* GetAuraScript() const
        {
            return new spell_kaelthas_burn_AuraScript();
        }
};

class spell_kaelthas_flame_strike : public SpellScriptLoader
{
	public:
		spell_kaelthas_flame_strike() : SpellScriptLoader("spell_kaelthas_flame_strike") { }

		class spell_kaelthas_flame_strike_AuraScript : public AuraScript
		{
			PrepareAuraScript(spell_kaelthas_flame_strike_AuraScript);

			bool Load()
			{
				return GetUnitOwner()->GetTypeId() == TYPEID_UNIT;
			}

			void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
			{
				GetUnitOwner()->RemoveAllAuras();
				GetUnitOwner()->CastSpell(GetUnitOwner(), SPELL_FLAME_STRIKE_DAMAGE, true);
				GetUnitOwner()->ToCreature()->DespawnOrUnsummon(2000);
			}

			void Register()
			{
				OnEffectRemove += AuraEffectRemoveFn(spell_kaelthas_flame_strike_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
			}
		};

		AuraScript* GetAuraScript() const
		{
			return new spell_kaelthas_flame_strike_AuraScript();
		}
};

class lapseTeleport : public BasicEvent
{
    public:
        lapseTeleport(Player* owner) : _owner(owner)
        {
        }

        bool Execute(uint64 /*execTime*/, uint32 /*diff*/)
        {
			if (_owner->IsBeingTeleportedNear())
				_owner->m_Events.AddEvent(new lapseTeleport(_owner), _owner->m_Events.CalculateTime(1));
			else if (!_owner->IsBeingTeleported())
			{
				_owner->CastSpell(_owner, SPELL_GRAVITY_LAPSE_KNOCKBACK, true);
				_owner->CastSpell(_owner, SPELL_GRAVITY_LAPSE_AURA, true);
			}
            return true;
        }

    private:
        Player* _owner;
};

class spell_kaelthas_gravity_lapse : public SpellScriptLoader
{
    public:
        spell_kaelthas_gravity_lapse() : SpellScriptLoader("spell_kaelthas_gravity_lapse") { }

        class spell_kaelthas_gravity_lapse_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_gravity_lapse_SpellScript);

			bool Load()
			{
				_currentSpellId = SPELL_GRAVITY_LAPSE_TELEPORT1;
				return true;
			}

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
				PreventHitEffect(effIndex);
				if (_currentSpellId < SPELL_GRAVITY_LAPSE_TELEPORT1+25)
					if (Player* target = GetHitPlayer())
					{
						GetCaster()->CastSpell(target, _currentSpellId++, true);
						target->m_Events.AddEvent(new lapseTeleport(target), target->m_Events.CalculateTime(1));
					}
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_kaelthas_gravity_lapse_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }

		private:
			uint32 _currentSpellId;
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_gravity_lapse_SpellScript();
        }
};

class spell_kaelthas_nether_beam : public SpellScriptLoader
{
    public:
        spell_kaelthas_nether_beam() : SpellScriptLoader("spell_kaelthas_nether_beam") { }

        class spell_kaelthas_nether_beam_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_nether_beam_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
				PreventHitEffect(effIndex);
				                   
				ThreatContainer::StorageType const & ThreatList = GetCaster()-> getThreatManager().getThreatList();
				std::list<Unit*> targetList;
                for (ThreatContainer::StorageType::const_iterator itr = ThreatList.begin(); itr != ThreatList.end(); ++itr)
                {
                    Unit* target = ObjectAccessor::GetUnit(*GetCaster(), (*itr)->getUnitGuid());
                    if (target && target->GetTypeId() == TYPEID_PLAYER)
                        targetList.push_back(target);
                }

				Trinity::Containers::RandomResizeList(targetList, 5);
				for (std::list<Unit*>::const_iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
					GetCaster()->CastSpell(*itr, SPELL_NETHER_BEAM_DAMAGE, true);
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_kaelthas_nether_beam_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_nether_beam_SpellScript();
        }
};

class spell_kaelthas_summon_nether_vapor : public SpellScriptLoader
{
    public:
        spell_kaelthas_summon_nether_vapor() : SpellScriptLoader("spell_kaelthas_summon_nether_vapor") { }

        class spell_kaelthas_summon_nether_vapor_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_kaelthas_summon_nether_vapor_SpellScript);

            void HandleScriptEffect(SpellEffIndex effIndex)
            {
				PreventHitEffect(effIndex);
				for (uint32 i = 0; i < 5; ++i)
					GetCaster()->SummonCreature(NPC_NETHER_VAPOR, GetCaster()->GetPositionX()+6*cos(i/5.0f*2*M_PI), GetCaster()->GetPositionY()+6*sin(i/5.0f*2*M_PI), GetCaster()->GetPositionZ()+7.0f+i, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 30000);
            }

            void Register()
            {
                OnEffectHitTarget += SpellEffectFn(spell_kaelthas_summon_nether_vapor_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
            }
        };

        SpellScript* GetSpellScript() const
        {
            return new spell_kaelthas_summon_nether_vapor_SpellScript();
        }
};

void AddSC_boss_kaelthas()
{
    new boss_kaelthas();
	new spell_kaelthas_kael_phase_two();
	new spell_kaelthas_remote_toy();
	new spell_kaelthas_summon_weapons();
	new spell_kaelthas_resurrection();
	new spell_kaelthas_mind_control();
	new spell_kaelthas_burn();
	new spell_kaelthas_flame_strike();
	new spell_kaelthas_gravity_lapse();
	new spell_kaelthas_nether_beam();
	new spell_kaelthas_summon_nether_vapor();
}
